/* This file is part of FireEscape.
 *
 * FireEscape is the intellectual work of Phillip Cheng & Allen Park.
 * All future releases or modifications of this program must include this
 * attribuation as according to the Creative Commons BY-SA license.
 *
 * FireEscape is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * FireEscape is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FireEscape.  If not, see <http://www.gnu.org/licenses/>.
 *
 * All modifications to this program should include this original statement.
 *
 * If you modify, distribute, or use the program , please contact
 * fireescape@kloudvine.com
 * http://code.google.com/p/fireescape/
 *
 * If you are interested in using FireEscape for your structure, please contact us
 * for support.
 *
 * If you do use FireEscape, please consider donating to the project.
 */
package fireescape;

import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.io.BufferedWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * This is the AI behind the genetics
 * @author chengP
 */
public class GeneticAI {

    public static int simulation = 1;
    public static int generations = 1;
    public static int geneticIterations = 1;
    public static int scrollVar = 0;
    //[SPACE]
    //private static final int sizeOfGen = 8;
    private static final double density = 0.003;
    //private static final double mutationfrequency = 0.004;
    //private static final String outputFilename = "output.txt";
    //[SPACE]
    private static List<Route> routes = new ArrayList<Route>();
    private static int numberRoutes;
    private static int genomeID = -1;
    //private static List<Genome> genome = new ArrayList<Genome>();
    private static double fastestTime = Double.POSITIVE_INFINITY;
    //private static int fastestHash = 0;
    private static ArrayList<String> write = new ArrayList<String>();
    private static int motherHash;
    private static int fatherHash;
    private static double motherTime;
    private static double fatherTime;
    private static BufferedWriter out;
    private static int lastWritten = 0;
    private static final Color darkBlue = new Color(0, 0, 204);
    private static int currentHash = 0;

    /**
     * Sets the routes
     * @param routes The new routes
     */
    public static void setRoutes(ArrayList<Route> routes) {
        GeneticAI.routes = routes;
    }

    /**
     * Sets the number of routes
     * @param numRoutes The new number of routes
     */
    public static void setNumRoutes(int numRoutes) {
        numberRoutes = numRoutes;
    }

    /**
     * Gets the number of routes
     * @return The number or routes
     */
    public static int getNumRoutes() {
        return numberRoutes;
    }

    /**
     * Adds a new route
     * @param route The new route
     */
    public static void addRoute(Route route) {
        routes.add(route);

    }

    /**
     * Returns a route
     * @param n The route ID
     * @return The route
     */
    public static Route getRoute(int n) {
        return routes.get(n);
    }

    /**
     * Initializes the genetic AI
     */
    public static void init() {
        Genome tempGenome = Client.getGenome();
        routes = tempGenome.getList();
        genomeID = tempGenome.getGenomeID();
        currentHash = tempGenome.getHash();
        motherHash = tempGenome.getMotherHash();
        fatherHash = tempGenome.getFatherHash();
        motherTime = tempGenome.getMotherTime();
        fatherTime = tempGenome.getFatherTime();
        reset();
    }

    /**
     * Adds new bots to the map
     */
    public static void addBots() {
        //Bot testBot = new Bot(-1, -1, -1, -1, -1);
        int totalBots = 0;
        for (int i = 0; i < numberRoutes; i++) {
            Route route = getRoute(i);
            int firstRoomID = route.getFirstRoom();
            Room firstRoom = Map.getRoom(firstRoomID);
            Exit firstExit = route.getNavPoint(0);
            //testBot.setRoomID(firstRoomID);
            //for (int j = 0; j < 1; j++) {
            int numberBots = (int) (firstRoom.area() * density);
            /*boolean giveUp = false;
            int botsMade = 0;*/
            for (int j = 0; j < numberBots; j++) {
                //int numberTriesAdd = 0;
                double newX = firstRoom.getX() + Math.random() * firstRoom.getWidth();
                double newY = firstRoom.getY() + Math.random() * firstRoom.getHeight();
                //while (overlapping(newX, newY, firstRoomID)) {
                while (!((firstExit.getRect().containsBot(newX, newY) && Map.getRoom(firstExit.connectedToID(firstRoomID)).totalNoOverlapCheck(newX, newY) && firstRoom.totalNoOverlapCheck(newX, newY)) || (firstRoom.botInRoom(newX, newY, firstExit) && firstRoom.totalNoOverlapCheck(newX, newY)))) {
                    newX = firstRoom.getX() + Math.random() * firstRoom.getWidth();
                    newY = firstRoom.getY() + Math.random() * firstRoom.getHeight();
                }
                /*while (!testBot.legitXMove(newX)) {
                newX = firstRoom.getX() + Math.random() * firstRoom.getWidth();
                }
                while (!testBot.legitYMove(newY)) {
                newY = firstRoom.getY() + Math.random() * firstRoom.getHeight();
                }*/
                /*numberTriesAdd++;
                if (numberTriesAdd > 10) {
                giveUp = true;
                break;
                }
                }
                .if (giveUp) {
                break;
                }*/
                Bot bot = new Bot(totalBots + j, i, firstRoomID, newX, newY);
                bot.setVelocity(BotAI.defaultVelocity);
                BotAI.addBot(bot);
                bot.checkOnExit();
                //botsMade++;
            }
            totalBots += numberBots;
        }
        System.out.println("Total number of bots: " + BotAI.getNumBots());
    }

    /**
     * Tests if the new coordinates overlaps any bots
     * @param x The new x coordinate
     * @param y The new y coordinate
     * @param roomID The room ID the bot should start in
     * @return True if the coordinates overlap with any bots, false if not
     */
    public static boolean overlapping(double x, double y, int roomID) {
        Room room = Map.getRoom(roomID);
        for (int i = 0; i < room.getNumBots(); i++) {
            Bot bot = room.getBot(i);
            if (FireEscapeGUI.distance(x, y, bot.getX(), bot.getY()) < 2 * BotAI.botRadius) {
                return true;
            }
        }
        return false;
    }

    /**
     * Runs the simulation. Is one iteration.
     */
    public static void run() {
        if (BotAI.botsEscaped + BotAI.botsDied < BotAI.getNumBots()) {
            BotAI.run();
            simulation++;
        } else {
            newGen();
        }
    }

    /**
     * Makes a new genome.
     */
    public static void newGen() {

        write.add(" ");
        write.add(String.valueOf(geneticIterations));
        write.add(String.valueOf(currentHash));
        if (motherHash == 0) {
            write.add("NO MOTHER EXISTS");
            write.add("NO FATHER EXISTS");
            //write.add("BOT PRDC BY BINARY FISSION");
        } else {
            write.add("mHash: " + motherHash);
            write.add("mTime: " + motherTime);

            write.add("FHash: " + fatherHash);
            write.add("FTime: " + fatherTime);
        }
        geneticIterations++;
        double sum = 0;
        /**for (Route r : routes) {
        sum += r.retrieveAverage();
        }
        double averagetime = sum / routes.size();**/
        double lastOut = 0;
        for (Route r : routes) {
            if (r.getSlowestTime() > lastOut) {
                lastOut = r.getSlowestTime();
            }
        }
        lastOut=lastOut+BotAI.botsDied*800;
        write.add("time: " + lastOut);
        Genome tempGenome = new Genome(currentHash, routes, lastOut, true, genomeID);
        Client.returnGenome(tempGenome);
        routes = new ArrayList<Route>();
        tempGenome = Client.getGenome();
        genomeID = tempGenome.getGenomeID();
        routes = tempGenome.getList();
        motherHash = tempGenome.getMotherHash();
        fatherHash = tempGenome.getFatherHash();
        motherTime = tempGenome.getMotherTime();
        fatherTime = tempGenome.getFatherTime();
        currentHash = tempGenome.getHash();
        reset();
    }

    /**
     * Resets the simulation
     */
    public static void reset() {
        Map.reset();
        BotAI.reset();
        addBots();
    }

    /**
     * Paints the information
     * @param g The Graphics2D object used for painting
     */
    public static void paint(Graphics2D g) {
        g.setColor(darkBlue);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        int y = 20;
        g.drawString("btsESC: " + FireEscape.escapeCount, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        g.drawString("btsDIE: " + FireEscape.deathCount, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        g.drawString("itr: " + geneticIterations, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        g.drawString("gen: " + generations, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        g.drawString("sims: " + simulation, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        g.drawString("rhash: " + currentHash, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        g.drawString("BstItr: " + fastestTime, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        y += 12;
        if (!FireEscapeGUI.stairOutput) {
            g.drawString("Floor: " + FireEscapeGUI.floorNum, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        } else {
            g.drawString("Staircase: " + FireEscapeGUI.stairNum, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        }
        y += 12;
        g.drawString("MD5ID: " + Client.getID(), (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
        for (int i = scrollVar; i < scrollVar + FireEscapeGUI.ySize / 12 - 7; i++) {
            if (i >= write.size()) {
                break;
            }
            String s = write.get(i);
            y += 12;
            g.drawString(s, (int) (FireEscapeGUI.scale * (FireEscapeGUI.xSize + 10)), y);
            if (lastWritten < write.size()) {
                lastWritten++;
            }
        }

        //stuff
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        Font temp = g.getFont();
        g.setColor(new Color((float) .827, (float) .827, (float) .827, (float) .4));
        g.fill(new RoundRectangle2D.Double((double) (FireEscapeGUI.scale * (FireEscapeGUI.xSize / 2 - 150)),
                (double) (FireEscapeGUI.scale * (FireEscapeGUI.ySize / 2 - 75)),
                300.0,
                150.0,
                10.0,
                10.0));
        g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18));
        g.setColor(Color.BLACK);
        float xc = (float) (FireEscapeGUI.scale * (FireEscapeGUI.xSize / 2));
        float yc = (float) (FireEscapeGUI.scale * (FireEscapeGUI.ySize / 2));
        if ((BotAI.botsEscaped + BotAI.botsDied) < (BotAI.getNumBots() * .99)) {
            g.drawString(String.valueOf((int) ((BotAI.botsEscaped + BotAI.botsDied) * 100 / BotAI.getNumBots())) + "% Done", xc - 40, yc - 15);
        } else {
            g.drawString("Communicating with servers...", xc - 130, yc +15);
        }
        g.setFont(temp);

    }
}
